package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/boyane/tool_pressure_test/common"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

var exPath string

func InitConfig() *common.Config {
	ex, err := os.Executable()
	if err != nil {
		panic(err)
	}
	exPath = filepath.Dir(ex)
	jsonFile, err := ioutil.ReadFile(exPath + "/config.json")
	if err != nil {
		fmt.Println(err.Error())
	}

	var _config *common.Config
	err = json.Unmarshal(jsonFile, &_config)
	if err != nil {
		fmt.Println("json.Unmarshal err ===>", err)
	}

	return _config
}

func main() {
	// 获取配置
	config := InitConfig()

	fmt.Println("开始测试...")
	fmt.Printf("测试并发次数：%d 每次并发量：%d \n", config.N, config.C)

	var wg sync.WaitGroup

	for _, path := range config.Urls {
		wg.Add(1)
		go Process(config, path, &wg)
	}
	wg.Wait()

	fmt.Println("测试结束...")
}

func Process(config *common.Config, url common.Urls, mWg *sync.WaitGroup) {
	defer func() {
		mWg.Done()
	}()
	startTime := time.Now().Unix()
	var failNum int64
	var sucNum int64
	// 开启锁

	var wg sync.WaitGroup
	for i := 0; i < config.N; i++ {
		wg.Add(1)
		var cWg sync.WaitGroup
		for j := 0; j < config.C; j++ {
			cWg.Add(1)
			go func() {
				defer cWg.Done()
				// 开始发送请求
				PostForm(config, url, &failNum, &sucNum)
			}()
		}
		cWg.Wait()
		wg.Done()
	}
	wg.Wait()

	endTime := time.Now().Unix()

	timeLen := endTime - startTime
	var qps int64
	if timeLen == 0 {
		qps = int64(config.N * config.C)
	} else {
		qps = int64(config.N*config.C) / timeLen
	}

	fmt.Printf("====> startTime: %d \t endTime: %d \t time: %d \t failNum: %d \t sucNum: %d \t QPS: %d  \t <==== %s \n", startTime, endTime, timeLen, failNum, sucNum, qps, url.Path)
}

// PostForm 发送http请求
func PostForm(config *common.Config, request common.Urls, failNum *int64, sucNum *int64) {
	var (
		status string
		body   string
		err    error
	)

	switch config.RequestType {
	case "json":
		status, body, err = postJson(config.Host+request.Path, request.Params)
		break
	case "form-data":
		status, body, err = postFormData(config.Host+request.Path, "POST", request.Params)
		break
	}

	if err != nil {
		*failNum++
		fmt.Println("http.PostForm fail ==>", err)
		return
	}
	*sucNum++
	WriteLog(fmt.Sprintf("host=%s, status=%s, response=%s", config.Host+request.Path, status, body))
	//fmt.Println("ret ===>", string(body))
	//fmt.Println(config.Host+request.Path+" ===> ", resp.Status)
}

// 发送form-data请求
func postFormData(url, method string, params []common.Params) (status string, ret string, err error) {
	payload := &bytes.Buffer{}
	writer := multipart.NewWriter(payload)
	for _, item := range params {
		_ = writer.WriteField(item.Key, item.Value)
	}
	err = writer.Close()
	if err != nil {
		fmt.Println(err)
		return
	}

	client := &http.Client{}
	req, err := http.NewRequest(method, url, payload)

	if err != nil {
		fmt.Println(err)
		return
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	status = res.Status
	ret = string(body)

	return
}

// 发送json请求
func postJson(url string, params []common.Params) (status string, ret string, err error) {
	parma := make(map[string]string)
	for _, item := range params {
		parma[item.Key] = item.Value
	}
	jsonString, err := json.Marshal(parma)
	if err != nil {
		fmt.Println("json.Marshal err: ", err)
		return
	}

	resp, err := http.Post(url, "application/json", strings.NewReader(string(jsonString)))
	// 关闭资源连接
	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("ioutil.ReadAll fail ==> ", err)
		return
	}

	status = resp.Status
	ret = string(body)

	return
}

// WriteLog 打印日志
func WriteLog(args ...interface{}) error {
	logPath := exPath + "/log/"
	logName := logPath + time.Now().Format("200601") + ".log"

	if _, err := os.Stat(logPath); err != nil {
		if !os.IsExist(err) {
			os.MkdirAll(logPath, os.ModePerm)
		}
	}

	logFile, err := os.OpenFile(logName, os.O_APPEND|os.O_RDWR|os.O_CREATE, os.ModePerm)
	if err != nil {
		fmt.Print("打开文件失败")
		return err
	}
	defer logFile.Close()
	tm := time.Now().Format("2006-01-02 15:04:05")
	logBody := fmt.Sprintf("%s star============================================>\r\n", tm)
	logFile.WriteString(logBody)
	logBody = fmt.Sprintf("%+v\r\n", args)
	logFile.WriteString(logBody)
	logBody = fmt.Sprintf("\r\n")
	logFile.WriteString(logBody)
	return nil
}
